﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using WooCoo.Reflection;
using WooCoo.Tools;

namespace WooCoo.DataPortal.Client
{
    /// <summary>
    /// QueryProvider实现
    /// </summary>
    internal sealed class DataPortalQueryProvider : IQueryProvider
    {
        /// <summary>数据门户代理类型,代理类实现转化适应的通信协议</summary>
        internal readonly IDataPortalProxy Proxy;

        /// <summary>Constructs a query provider based on the context passed in </summary>
        /// <param name="context">The context for the query provider</param>
        internal DataPortalQueryProvider(IDataPortalProxy proxy)
        {
            this.Proxy = proxy;
        }

        #region IQueryProvider implementation

        /// <summary>Factory method for creating DataServiceOrderedQuery based on expression </summary>
        /// <param name="expression">The expression for the new query</param>
        /// <returns>new DataServiceQuery</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "parameters are validated against null via CheckArgumentNull")]
        public IQueryable CreateQuery(Expression expression)
        {
            Check.CheckArgumentNull(expression, "expression");
            Type et = TypeSystem.GetElementType(expression.Type);
            Type qt = typeof(DataPortalQuery<>.DataPortalOrderedQuery).MakeGenericType(et);
            object[] args = new object[] { expression, this };

            ConstructorInfo ci = qt.GetConstructor(
                BindingFlags.NonPublic | BindingFlags.Instance,
                null,
                new Type[] { typeof(Expression), typeof(DataPortalQueryProvider) },
                null);

            return (IQueryable)ci.Invoke(args);
        }

        /// <summary>Factory method for creating DataServiceOrderedQuery based on expression </summary>
        /// <typeparam name="TElement">generic type</typeparam>
        /// <param name="expression">The expression for the new query</param>
        /// <returns>new DataServiceQuery</returns>
        public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
        {
            Check.CheckArgumentNull(expression, "expression");
            return new DataPortalQuery<TElement>.DataPortalOrderedQuery(expression, this);
        }

        /// <summary>根据表达式执行数据门户查询</summary>
        /// <param name="expression">查询表达式</param>
        /// <returns>结果</returns>
        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining | System.Runtime.CompilerServices.MethodImplOptions.NoOptimization)]
        public object Execute(Expression expression)
        {
            Check.CheckArgumentNull(expression, "expression");

            MethodInfo mi = typeof(DataPortalQueryProvider).GetMethod("ReturnSingleton", BindingFlags.NonPublic | BindingFlags.Instance);
            return mi.MakeGenericMethod(expression.Type).Invoke(this, new object[] { expression });
        }

        /// <summary>根据表达式执行数据门户查询</summary>
        /// <typeparam name="TResult">泛型参数</typeparam>
        /// <param name="expression">查询表达式</param>
        /// <returns>泛型结果</returns>
        public TResult Execute<TResult>(Expression expression)
        {
            Check.CheckArgumentNull(expression, "expression");
            return ReturnSingleton<TResult>(expression);
        }

        #endregion

        /// <summary>非列表的数据门户查询结果</summary>
        /// <typeparam name="TElement">泛型参数</typeparam>
        /// <param name="expression">查询表达式</param>
        /// <returns>单一的结果值</returns>
        internal TElement ReturnSingleton<TElement>(Expression expression)
        {
            IQueryable<TElement> query = new DataPortalQuery<TElement>.DataPortalOrderedQuery(expression, this);

            MethodCallExpression mce = expression as MethodCallExpression;
            Debug.Assert(mce != null, "mce != null");

            SequenceMethod sequenceMethod;
            if (ReflectionUtil.TryIdentifySequenceMethod(mce.Method, out sequenceMethod))
            {
                switch (sequenceMethod)
                {
                    case SequenceMethod.Single:
                        return query.AsEnumerable().Single();
                    case SequenceMethod.SingleOrDefault:
                        return query.AsEnumerable().SingleOrDefault();
                    case SequenceMethod.First:
                        return query.AsEnumerable().First();
                    case SequenceMethod.FirstOrDefault:
                        return query.AsEnumerable().FirstOrDefault();
#if !ASTORIA_LIGHT
                    case SequenceMethod.LongCount:
                    case SequenceMethod.Count:
                        return (TElement)Convert.ChangeType(((DataPortalQuery<TElement>)query).GetQuerySetCount(this.Proxy), typeof(TElement), System.Globalization.CultureInfo.InvariantCulture.NumberFormat);
#endif
                    default:
                        throw Error.NotSupported(mce.Method.Name);
                }
            }

            // Should never get here - should be caught by expression compiler.
            Debug.Assert(false, "Not supported singleton operator not caught by Resource Binder");
            throw Error.NotSupported(mce.Method.Name);
        }
    }
}
